Skip to content

feat(python): add uv package manager support (TC-3855)#428

Merged
a-oren merged 9 commits into
guacsec:mainfrom
a-oren:TC-3855
Apr 27, 2026
Merged

feat(python): add uv package manager support (TC-3855)#428
a-oren merged 9 commits into
guacsec:mainfrom
a-oren:TC-3855

Conversation

@a-oren

@a-oren a-oren commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add PythonUvProvider and PythonProviderFactory for automatic uv package manager detection via uv.lock presence
  • Dependency resolution uses uv pip list --format=json + uv pip show for CycloneDX SBOM generation (stack + component)
  • Extract shared TOML parsing into PyprojectTomlUtils, refactor both PythonPyprojectProvider and PythonUvProvider to use it
  • Fix PEP 508 extras stripping bug in getDependencyName() (e.g. requests[security]requests) affecting all Python providers
  • Document uv support, TRUSTIFY_DA_UV_PATH, and CLI examples in README

Test plan

  • mvn test -Dtest="Python_Uv_Provider_Test" -Dskip.junit_platform=true — 15 uv provider tests pass
  • mvn test -Dtest="Python_Pyproject_Provider_Test" -Dskip.junit_platform=true — 22 existing pyproject tests pass (regression)
  • mvn test -Dtest="PythonControllerRealEnvTest#get_Dependency_Name*" -Dskip.junit_platform=true — extras fix tests pass
  • mvn spotless:check — formatting passes
  • Manual CLI test: java -jar trustify-da-java-client-cli.jar sbom /path/to/uv-project/pyproject.toml produces correct CycloneDX SBOM

Acceptance Criteria

  • PythonProviderFactory.create() returns PythonUvProvider when uv.lock exists
  • PythonProviderFactory.create() falls back to PythonPyprojectProvider when no uv.lock
  • PythonUvProvider discovers uv binary via Operations.getExecutable("uv", "--version")
  • Custom binary path supported via TRUSTIFY_DA_UV_PATH
  • validateLockFile() throws IllegalStateException when uv.lock missing
  • provideStack() and provideComponent() generate valid CycloneDX SBOMs
  • Existing requirements.txt and pyproject.toml flows unchanged
  • #trustify-da-ignore / #exhortignore patterns work in uv context
  • README documents uv support

🤖 Generated with Claude Code

Summary by Sourcery

Add uv-based Python provider selection and shared pyproject.toml utilities while preserving existing pip/pyproject behavior.

New Features:

  • Introduce PythonUvProvider to generate CycloneDX SBOMs for uv-managed pyproject.toml projects based on uv export output.
  • Add PythonProviderFactory to automatically choose between uv and pip-based pyproject providers using uv.lock detection, including workspace-aware lock-file lookup.

Bug Fixes:

  • Ensure Python dependency name parsing strips PEP 508 extras before extracting the base package name, preventing incorrect identifiers like requests[security].

Enhancements:

  • Refactor pyproject.toml parsing and metadata extraction into shared PyprojectTomlUtils used by both pip and uv Python providers.
  • Extend Python pyproject provider to reuse shared canonicalization, license lookup, and ignore-pattern handling logic.

CI:

  • Install the uv tool in the pull request workflow to support uv-related tests.

Documentation:

  • Document uv package manager support for Python, including automatic lock-file based selection, custom TRUSTIFY_DA_UV_PATH configuration, and CLI SBOM usage examples.

Tests:

  • Add comprehensive unit tests for PythonUvProvider, including graph parsing, SBOM generation, and ignore patterns for uv projects.
  • Add tests for PythonProviderFactory lock-file discovery across workspaces and for dependency name parsing with PEP 508 extras in real Python environments.

a-oren and others added 2 commits April 20, 2026 15:04
…ackage manager support

TC-3855

Add automatic detection and support for Python projects managed by the uv
package manager. When uv.lock is present alongside pyproject.toml, the new
PythonProviderFactory selects PythonUvProvider (using uv pip list/show for
dependency resolution) instead of the pip-based PythonPyprojectProvider.

- Add PythonProviderFactory with Map-based lock file detection pattern
- Add PythonUvProvider with CycloneDX SBOM generation (stack + component)
- Add PyprojectTomlUtils shared utility to deduplicate TOML parsing logic
- Refactor PythonPyprojectProvider to use PyprojectTomlUtils
- Fix PEP 508 extras stripping in getDependencyName (e.g. requests[security])
- Update Ecosystem.resolveProvider() to delegate to PythonProviderFactory
- Add test fixtures and 15 unit tests for the uv provider
- Document uv support, TRUSTIFY_DA_UV_PATH, and CLI examples in README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assisted-by: Claude Code
TC-3855

The PythonUvProvider unit tests require the uv binary to be available,
since the constructor eagerly validates it via Operations.getExecutable().
This follows the same pattern as other providers (cargo, go, gradle).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assisted-by: Claude Code
@a-oren
a-oren requested a review from ruromero April 20, 2026 13:14
@a-oren

a-oren commented Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

/review

1 similar comment
@a-oren

a-oren commented Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

/review

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR this one is not trivial. I made some comments.

Comment thread src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java Outdated
Comment thread src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java Outdated
Comment thread src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java Outdated
Comment thread README.md Outdated
a-oren and others added 2 commits April 23, 2026 12:51
Replace the two-command approach (uv pip list + uv pip show) with a
single uv export --format requirements.txt --frozen --no-hashes
--no-dev --no-emit-project, aligning with the JS client implementation.

Also extract duplicated canonicalize() method from PythonUvProvider and
PythonPyprojectProvider into PyprojectTomlUtils, and convert UvPackage
and UvDependencyData from static final classes to records.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@a-oren

a-oren commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@ruromero Thanks for the review

@a-oren
a-oren requested a review from ruromero April 23, 2026 10:05

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to look for the lock file path. The other changes are covered and look good.

… members

The factory and validateLockFile only checked the manifest directory for
uv.lock, which fails for uv workspace members where the lock file lives
at the workspace root. Add parent directory walk-up matching the JS
client's _findLockFileDir pattern, with TRUSTIFY_DA_WORKSPACE_DIR
override, git root boundary, and uv workspace root detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@a-oren
a-oren requested a review from ruromero April 23, 2026 13:22
@ruromero

Copy link
Copy Markdown
Collaborator

@sourcery-ai review

@sourcery-ai

sourcery-ai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds uv-based Python dependency resolution alongside existing pip/pyproject support, refactors shared pyproject.toml handling into utilities, fixes PEP 508 extras parsing for dependency names, wires uv into provider selection/CI/docs, and adds comprehensive tests for the new behavior.

Sequence diagram for SBOM generation using uv for a pyproject.toml manifest

sequenceDiagram
    actor User
    participant CLI as JavaClientCLI
    participant Ecosystem as Ecosystem
    participant PythonProviderFactory as PythonProviderFactory
    participant PythonUvProvider as PythonUvProvider
    participant Operations as Operations
    participant SbomFactory as SbomFactory
    participant Sbom as Sbom

    User->>CLI: sbom /path/to/uv-project/pyproject.toml
    CLI->>Ecosystem: resolveProvider(manifestPath)
    Ecosystem->>PythonProviderFactory: create(manifestPath)
    PythonProviderFactory->>PythonProviderFactory: check manifestDir for uv.lock
    alt uv.lock found
      PythonProviderFactory-->>Ecosystem: new PythonUvProvider(manifestPath)
    else no uv.lock
      PythonProviderFactory->>PythonProviderFactory: findLockFileDirInParents(startDir)
      alt parent with uv.lock found
        PythonProviderFactory-->>Ecosystem: new PythonUvProvider(manifestPath)
      else no lock file
        PythonProviderFactory-->>Ecosystem: new PythonPyprojectProvider(manifestPath)
      end
    end

    CLI->>PythonUvProvider: provideStack()
    PythonUvProvider->>PythonUvProvider: validateLockFile(manifestDir)
    PythonUvProvider->>PythonUvProvider: rejectPoetryDependencies()
    PythonUvProvider->>PythonUvProvider: collectIgnoredDeps()

    PythonUvProvider->>Operations: getExecutable("uv", "--version")
    Operations-->>PythonUvProvider: uvExecutable path

    PythonUvProvider->>PythonUvProvider: getUvExportOutput(manifestDir)
    PythonUvProvider->>Operations: runProcessGetFullOutput(cwd, cmd, env)
    Operations-->>PythonUvProvider: ProcessExecOutput(uv export text)

    PythonUvProvider->>PythonUvProvider: parseUvExport(exportOutput)
    PythonUvProvider->>PythonUvProvider: getRootComponentName()
    PythonUvProvider->>PythonUvProvider: getRootComponentVersion()
    PythonUvProvider->>PythonUvProvider: readLicenseFromManifest()

    PythonUvProvider->>SbomFactory: newInstance(BelongingCondition.PURL, "sensitive")
    SbomFactory-->>PythonUvProvider: Sbom

    PythonUvProvider->>Sbom: addRoot(purl, license)
    loop for each direct dependency
      PythonUvProvider->>PythonUvProvider: addDependencyTree(rootPurl, pkg, graph, sbom, visited)
      PythonUvProvider->>Sbom: addDependency(source, target, null)
    end

    PythonUvProvider->>PythonUvProvider: handleIgnoredDependencies(manifestContent, sbom)
    PythonUvProvider->>Sbom: getAsJsonString()
    PythonUvProvider-->>CLI: Content(sbomJson, CYCLONEDX_MEDIA_TYPE)
    CLI-->>User: SBOM JSON output
Loading

Class diagram for new Python uv support and shared pyproject utilities

classDiagram
    direction LR

    class PythonProvider {
      <<abstract>>
      Path manifestPath
      +PythonProvider(Path manifestPath)
      +Content provideStack()
      +Content provideComponent()
      +void validateLockFile(Path lockFileDir)
      +PackageURL toPurl(String name, String version)
      +void handleIgnoredDependencies(String manifestContent, Sbom sbom)
      +String getRootComponentName()
      +String getRootComponentVersion()
      +String readLicenseFromManifest()
      +Set~PackageURL~ getIgnoredDependencies(String manifestContent)
    }

    class PythonPyprojectProvider {
      -TomlParseResult cachedToml
      +PythonPyprojectProvider(Path manifestPath)
      +Content provideStack()
      +Content provideComponent()
      +String readLicenseFromManifest()
      +Set~PackageURL~ getIgnoredDependencies(String manifestContent)
      -void rejectPoetryDependencies()
      -void collectIgnoredDeps()
      -TomlParseResult getToml()
    }

    class PythonUvProvider {
      <<final>>
      +static String LOCK_FILE
      +static String PROP_TRUSTIFY_DA_UV_EXPORT
      -String uvExecutable
      -Set~String~ collectedIgnoredDeps
      -TomlParseResult cachedToml
      +PythonUvProvider(Path manifest)
      +void validateLockFile(Path lockFileDir)
      +Content provideStack()
      +Content provideComponent()
      +String readLicenseFromManifest()
      +Set~PackageURL~ getIgnoredDependencies(String manifestContent)
      -void addDependencyTree(PackageURL source, UvPackage pkg, Map~String, UvPackage~ graph, Sbom sbom, Set~String~ visited)
      -UvDependencyData parseUvExport(String exportOutput)
      -static void recordViaParent(String parentName, String childKey, String projectName, List~String~ directDeps, List~String[]~ parentChildPairs)
      -String getUvExportOutput(Path manifestDir)
      -TomlParseResult getToml()
      -void rejectPoetryDependencies()
      -void collectIgnoredDeps()
    }

    class PythonProviderFactory {
      <<final>>
      -static Map~String, Function~Path, PythonProvider~~ PYTHON_PROVIDERS
      +static PythonProvider create(Path manifestPath)
      +static Path findLockFileDirInParents(Path startDir)
    }

    class PyprojectTomlUtils {
      <<final>>
      +static TomlParseResult parseToml(Path manifest)
      +static Set~String~ collectIgnoredDeps(Path manifest, TomlParseResult toml)
      +static String getProjectName(TomlParseResult toml)
      +static String getProjectVersion(TomlParseResult toml)
      +static String getLicense(TomlParseResult toml)
      +static boolean hasPoetryDependencies(TomlParseResult toml)
      +static String canonicalize(String name)
      +static boolean isUvWorkspaceRoot(Path dir)
    }

    class PythonControllerBase {
      +static String getDependencyName(String dep)
      +String getDependencyNameShow(String pipShowOutput)
    }

    class Ecosystem {
      -static Provider resolveProvider(Path manifestPath)
    }

    class Operations {
      +static String getExecutable(String binaryName, String versionArg)
      +static Operations.ProcessExecOutput runProcessGetFullOutput(Path cwd, String[] cmd, Map~String,String~ env)
      +static java.util.Optional~String~ getGitRootDir(String startDir)
    }

    class Environment {
      +static String get(String key)
    }

    class SbomFactory {
      +static Sbom newInstance()
      +static Sbom newInstance(Sbom.BelongingCondition condition, String sensitivity)
    }

    class Sbom {
      +static enum BelongingCondition
      +void addRoot(PackageURL purl, String license)
      +PackageURL getRoot()
      +void addDependency(PackageURL source, PackageURL target, String scope)
      +StringBuilder getAsJsonString()
    }

    class LicenseUtils {
      +static String readLicenseFile(Path manifestPath)
    }

    class Provider {
      <<interface>>
      +Content provideStack()
      +Content provideComponent()
      +void validateLockFile(Path lockFileDir)
    }

    class Content {
      +Content(byte[] data, String mediaType)
    }

    class UvPackage {
      <<record>>
      +String name
      +String version
      +List~String~ children
    }

    class UvDependencyData {
      <<record>>
      +List~String~ directDeps
      +Map~String, UvPackage~ graph
    }

    class IgnorePatternDetector {
      +static boolean containsIgnorePattern(String line)
    }

    PythonProvider <|-- PythonPyprojectProvider
    PythonProvider <|-- PythonUvProvider

    Provider <|.. PythonProvider

    Ecosystem --> PythonProviderFactory : uses
    PythonProviderFactory --> PythonUvProvider : returns
    PythonProviderFactory --> PythonPyprojectProvider : returns

    PythonUvProvider --> PyprojectTomlUtils : uses
    PythonUvProvider --> Operations : uses
    PythonUvProvider --> Environment : uses
    PythonUvProvider --> SbomFactory : uses
    PythonUvProvider --> Sbom : uses
    PythonUvProvider --> LicenseUtils : uses
    PythonUvProvider --> PythonControllerBase : uses

    PythonPyprojectProvider --> PyprojectTomlUtils : uses

    PythonProviderFactory --> Operations : uses
    PythonProviderFactory --> Environment : uses
    PythonProviderFactory --> PyprojectTomlUtils : uses

    PyprojectTomlUtils --> IgnorePatternDetector : uses

    SbomFactory --> Sbom : creates

    Content --> Api : mediaType

    class Api {
      +static String CYCLONEDX_MEDIA_TYPE
    }
Loading

File-Level Changes

Change Details Files
Introduce a uv-based Python provider and factory-based selection using lock files.
  • Add PythonUvProvider that runs uv export to produce a requirements-style graph, parses # via comments into a dependency graph, and generates CycloneDX SBOMs for stack and component views.
  • Implement PythonProviderFactory to choose between PythonUvProvider and PythonPyprojectProvider based on presence of uv.lock in the manifest directory, parent workspace roots, or an explicit workspace override, and update Ecosystem.resolveProvider to use it.
  • Implement lock-file discovery with workspace-awareness, including uv workspace roots and TRUSTIFY_DA_WORKSPACE_DIR override, and validate that uv.lock exists for uv-based flows.
src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java
src/main/java/io/github/guacsec/trustifyda/providers/PythonProviderFactory.java
src/main/java/io/github/guacsec/trustifyda/tools/Ecosystem.java
src/test/java/io/github/guacsec/trustifyda/providers/Python_Uv_Provider_Test.java
src/test/java/io/github/guacsec/trustifyda/providers/PythonProviderFactoryLockFileTest.java
Refactor shared pyproject.toml utilities and reuse them in existing and new providers.
  • Extract TOML parsing, project metadata accessors, license extraction, poetry detection, ignore-pattern collection, name canonicalization, and uv-workspace detection into PyprojectTomlUtils.
  • Update PythonPyprojectProvider to use PyprojectTomlUtils for TOML parsing, project metadata, canonicalization, poetry detection, and ignored dependency collection, removing local helpers and delegating to the shared utility class.
  • Ensure uv provider also reuses PyprojectTomlUtils for canonicalization, TOML parsing, metadata, license reading, ignore detection, and uv workspace detection.
src/main/java/io/github/guacsec/trustifyda/utils/PyprojectTomlUtils.java
src/main/java/io/github/guacsec/trustifyda/providers/PythonPyprojectProvider.java
src/test/java/io/github/guacsec/trustifyda/providers/Python_Pyproject_Provider_Test.java
src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java
Fix PEP 508 extras handling for dependency names across Python flows.
  • Update PythonControllerBase.getDependencyName to strip extras segments in requirement strings (e.g., [security]) before parsing version constraints, ensuring canonical package names.
  • Add regression tests in PythonControllerRealEnvTest to validate extras handling in getDependencyName for multiple patterns and with environment markers.
  • Use the improved dependency-name parsing from PythonControllerBase when deriving ignored dependencies in the uv provider.
src/main/java/io/github/guacsec/trustifyda/utils/PythonControllerBase.java
src/test/java/io/github/guacsec/trustifyda/utils/PythonControllerRealEnvTest.java
src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java
Update CI, fixtures, and documentation for uv support and configuration.
  • Install uv in the GitHub PR workflow so uv-based tests can run in CI.
  • Add pyproject/uv fixtures (with and without ignore annotations) including pyproject.toml, uv.lock, and captured uv_export.txt output to drive unit tests without invoking uv.
  • Document uv as a supported Python package manager, add TRUSTIFY_DA_UV_PATH to configuration docs, and include CLI examples for generating SBOMs from uv-managed projects.
.github/workflows/pr.yml
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv/pyproject.toml
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv/uv.lock
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv/uv_export.txt
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv_ignore/pyproject.toml
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv_ignore/uv.lock
src/test/resources/tst_manifests/pip/pip_pyproject_toml_uv_ignore/uv_export.txt
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/main/java/io/github/guacsec/trustifyda/providers/PythonUvProvider.java" line_range="233-239" />
<code_context>
+    return new UvDependencyData(directDeps, packages);
+  }
+
+  private static void recordViaParent(
+      String parentName,
+      String childKey,
+      String projectName,
+      List<String> directDeps,
+      List<String[]> parentChildPairs) {
+    String parentKey = PyprojectTomlUtils.canonicalize(parentName);
+    if (parentKey.equals(projectName)) {
+      if (!directDeps.contains(childKey)) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Parent names from `# via` lines likely include version or extra annotations and should be normalized before canonicalization.

In `recordViaParent`, `parentName` comes directly from the `# via` comment and can include versions/extras (e.g., `foo (>=1.0)`, `foo[extra]`). Canonicalizing that raw value produces keys that won’t match the bare names used for package lines, so some parent→child edges may be lost. Strip versions/extras from `parentName` first (as you do for package lines), then canonicalize the cleaned name before using it as a key.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

a-oren and others added 2 commits April 26, 2026 13:43
- Skip self-referencing editable installs (root project as its own dep)
- Require both name and version for editable installs (skip if missing)
- Fall back to tool.poetry.name/version for editable install metadata
- Validate bare package names in # via comments (skip version specifiers/extras)
- Throw on unpinned versions in uv export output
- Prevent workspace root walk-up from escaping into parent workspace

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@a-oren
a-oren requested a review from ruromero April 26, 2026 10:50

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Adva. Looks good

@a-oren

a-oren commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

Verification Report for TC-3855 (commit 008eca0)

Check Result Details
Review Feedback PASS 8 threads found (5 code change requests, 3 suggestions); all addressed in follow-up commits. No sub-tasks created.
Root-Cause Investigation N/A No sub-tasks created — nothing to investigate.
Scope Containment WARN All 9 expected files present; 9 additional files changed (CI workflow, PyprojectTomlUtils extraction, additional tests/fixtures).
Diff Size PASS +1,619 / -63 across 18 files. Proportionate for a new provider with tests and fixtures.
Commit Traceability PASS Primary commits reference TC-3855; all work on TC-3855 branch.
Sensitive Patterns PASS No passwords, secrets, API keys, or private keys detected.
CI Status PASS All 45 checks pass (build, unit tests, integration tests across ubuntu/macos/windows).
Acceptance Criteria PASS 13/13 criteria met.
Test Quality WARN Some test methods are parameterization candidates (provideStack/provideComponent variants, canonicalize duplication across files). Most test methods in Python_Uv_Provider_Test (0/22) and Python_Pyproject_Provider_Test (0/23) lack JavaDoc comments.
Verification Commands N/A Not run locally (CI confirms tests and spotless pass).

Overall: WARN

Issues requiring attention:

  • Scope: 9 out-of-scope files changed — justified by reviewer-requested refactoring (PyprojectTomlUtils extraction, uv export switch, workspace support) and CI updates. All changes are relevant to the feature.
  • Test Quality (advisory): Test parameterization opportunities exist. test_canonicalize is duplicated across Python_Uv_Provider_Test and Python_Pyproject_Provider_Test. Most test methods lack JavaDoc comments.

This comment was AI-generated by sdlc-workflow/verify-pr v0.5.11.

@a-oren
a-oren merged commit 601178a into guacsec:main Apr 27, 2026
45 checks passed
@a-oren
a-oren deleted the TC-3855 branch April 27, 2026 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants